package p.ithorns.sample.websocket.core;

import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import p.ithorns.framework.common.utils.StringUtil;

import java.util.concurrent.ConcurrentHashMap;

/**
 * Socket Channel通道管理
 *
 * @author ithorns
 * @date 2020/4/20 11:10
 */
@Slf4j
public class ChannelSupervise {

    /**
     * Channel组
     * ImmediateEventExecutor INSTANCE
     */
    private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * Client -> Channel
     */
    private final static ConcurrentHashMap<String, ChannelId> CLIENT_ID_MAP = new ConcurrentHashMap<>();

    // Channel -> Client 使用 {@link Channel} 的 {@link io.netty.util.Attribute} 替代
    // private final static ConcurrentHashMap<String, String> CHANNEL_ID_MAP = new ConcurrentHashMap<>();

    public static <T> void setChannelValue(Channel channel, String name, T value) {
        AttributeKey<T> sessionIdKey = AttributeKey.valueOf(name);
        channel.attr(sessionIdKey).set(value);
    }

    public static <T> T getChannelValue(Channel channel, String name) {
        AttributeKey<T> sessionIdKey = AttributeKey.valueOf(name);
        return channel.attr(sessionIdKey).get();
    }

    private static <T> void removeChannelValue(Channel channel, String name) {
        AttributeKey<T> sessionIdKey = AttributeKey.valueOf(name);
        // 不要使用remove方法，见API
        channel.attr(sessionIdKey).set(null);
    }

    /**
     * 注册客户端Channel
     * <p>
     * 客户端与Channel的关联关系：clientId->channelId->channel
     * 通过clientId获取Channel的方式: CLIENT_ID_MAP -> GLOBAL_GROUP
     *
     * @param clientId -客户端ID
     * @param channel  -Channel
     */
    public static void register(String clientId, Channel channel) {
        if (!StringUtil.hasText(clientId)) {
            return;
        }

        // GLOBAL_GROUP 缓存 Channel
        ChannelId channelId = channel.id();
        Channel ch = GLOBAL_GROUP.find(channelId);
        if (null == ch) {
            GLOBAL_GROUP.add(channel);
        }

        // 客户端ID 映射 Channel
        CLIENT_ID_MAP.put(clientId, channelId);

        // 反向映射 channel -> clientId: 直接在Channel上缓存客户端ID
        setChannelValue(channel, getChannelId(channel), clientId);
    }


    /**
     * 移除Channel
     *
     * @param channel -
     */
    public static void removeChannel(Channel channel) {
        GLOBAL_GROUP.remove(channel);

        String channelId = getChannelId(channel);

        String clientId = getChannelValue(channel, channelId);
        if (clientId != null) {
            CLIENT_ID_MAP.remove(clientId);

            removeChannelValue(channel, channelId);
        }
    }


    private static String getChannelId(Channel channel) {
        return channel.id().asShortText();
    }


    /**
     * 指定客户端发送Json消息
     *
     * @param clientId -客户端ID
     * @param msg      -消息数据
     */
    public static void send(String clientId, Object msg) {
        ChannelId channelId = CLIENT_ID_MAP.get(clientId);
        if (null != channelId) {
            Channel channel = GLOBAL_GROUP.find(channelId);
            if (null != channel) {
                channel.writeAndFlush(msg);
            }
        }
    }

}